package com.xuecheng.base.utils;

import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class FFMpegUtils {

    // 静态配置（需要在应用启动时初始化）
    private static String FFMPEG_PATH;
    private static long FFMPEG_TIMEOUT = 30; // 默认超时30秒

    /**
     * 初始化FFmpeg配置
     * @param ffmpegPath FFmpeg可执行文件路径
     * @param timeout 超时时间(秒)，如果为0则使用默认值
     */
    public static void init(String ffmpegPath, long timeout) {
        if (ffmpegPath == null || ffmpegPath.isEmpty()) {
            throw new IllegalArgumentException("FFmpeg路径不能为空");
        }
        FFMPEG_PATH = normalizePath(ffmpegPath);
        if (timeout > 0) {
            FFMPEG_TIMEOUT = timeout;
        }
    }

    // 本地文件处理（静态方法）
    public static double getVideoDuration(String videoFilePath) throws IOException, InterruptedException {
        checkInitialized();
        String[] command = createFfmpegCommand(videoFilePath);
        return processDurationCommand(command);
    }

    // MultipartFile处理（静态方法）
    public static double getVideoDuration(MultipartFile file) throws IOException, InterruptedException {
        checkInitialized();
        Path tempFile = null;
        try {
            tempFile = saveMultipartFileToTemp(file);
            return getVideoDuration(tempFile.toString());
        } finally {
            // 清理临时文件
            if (tempFile != null) {
                Files.deleteIfExists(tempFile);
            }
        }
    }

    private static void checkInitialized() {
        if (FFMPEG_PATH == null) {
            throw new IllegalStateException("FFMpegUtils未初始化！请先调用init()方法");
        }
    }

    private static String[] createFfmpegCommand(String filePath) {
        return new String[]{
                FFMPEG_PATH,
                "-i", filePath,
                "-c", "copy", // 不进行编解码，提高性能
                "-f", "null",
                "-"
        };
    }

    private static Path saveMultipartFileToTemp(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String suffix = "";
        if (originalFilename != null && originalFilename.contains(".")) {
            suffix = originalFilename.substring(originalFilename.lastIndexOf('.'));
        }

        Path tempFile = Files.createTempFile("ffmpeg_", suffix);

        // 写入文件内容
        try (InputStream inputStream = file.getInputStream()) {
            Files.copy(inputStream, tempFile, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
        }

        return tempFile;
    }

    private static double processDurationCommand(String[] command) throws IOException, InterruptedException {
        Process process = null;
        try {
            process = new ProcessBuilder(command)
                    .redirectErrorStream(true)  // 合并标准输出和错误流
                    .start();

            // 读取输出
            String output = captureStreamWithTimeout(process.getInputStream(), FFMPEG_TIMEOUT, TimeUnit.SECONDS);

            // 等待进程完成（带超时）
            boolean completed = process.waitFor(FFMPEG_TIMEOUT, TimeUnit.SECONDS);
            if (!completed) {
                throw new IOException("FFmpeg执行超时");
            }

            int exitCode = process.exitValue();
            if (exitCode != 0) {
                // 获取详细的错误输出
                String errorOutput = captureErrorOutput(process);
                throw new IOException("FFmpeg执行失败！\n" +
                        "命令: " + String.join(" ", command) + "\n" +
                        "退出码: " + exitCode + "\n" +
                        "错误输出: " + errorOutput);
            }

            double duration = parseDuration(output);
            if (duration < 0) {
                throw new IOException("无法解析视频时长！输出内容:\n" + output);
            }
            return duration;
        } finally {
            // 确保进程终止
            if (process != null && process.isAlive()) {
                process.destroyForcibly();
            }
        }
    }

    // 捕获错误输出
    private static String captureErrorOutput(Process process) throws IOException {
        try (InputStream errorStream = process.getErrorStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            return sb.toString();
        }
    }

    // 带超时的流读取方法
    private static String captureStreamWithTimeout(InputStream inputStream, long timeout, TimeUnit unit)
            throws IOException {

        StringBuilder sb = new StringBuilder();
        Pattern durationPattern = Pattern.compile("Duration:\\s*(\\d+):(\\d+):(\\d+\\.\\d+)");
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

        long startTime = System.currentTimeMillis();
        long timeoutMillis = unit.toMillis(timeout);

        String line;
        while ((line = readLineWithTimeout(reader, startTime, timeoutMillis)) != null) {
            sb.append(line).append("\n");
            // 检查是否找到持续时间信息
            if (durationPattern.matcher(line).find()) {
                break; // 提前结束
            }
        }

        return sb.toString();
    }

    // 带超时读取行
    private static String readLineWithTimeout(BufferedReader reader, long startTime, long timeoutMillis)
            throws IOException {

        while (!reader.ready()) {
            // 检查超时
            if (System.currentTimeMillis() - startTime > timeoutMillis) {
                throw new IOException("读取FFmpeg输出超时");
            }
            // 短暂等待
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new IOException("读取线程被中断");
            }
        }
        return reader.readLine();
    }

    // 解决Windows路径问题
    private static String normalizePath(String rawPath) {
        // 处理Windows路径空格问题
        if (isWindows() && rawPath.contains(" ")) {
            // Windows下路径包含空格时需用双引号包裹
            return "\"" + rawPath.replace("/", "\\") + "\"";
        }
        return rawPath;
    }

    private static boolean isWindows() {
        return System.getProperty("os.name").toLowerCase().contains("win");
    }

    // 解析视频时长
    private static double parseDuration(String output) {
        // 匹配 "Duration: HH:MM:SS.ms" 格式的时间字符串
        Pattern pattern = Pattern.compile("Duration:\\s*(\\d+):(\\d+):(\\d+\\.\\d+)");
        Matcher matcher = pattern.matcher(output);

        if (matcher.find()) {
            int hours = Integer.parseInt(matcher.group(1));
            int minutes = Integer.parseInt(matcher.group(2));
            double seconds = Double.parseDouble(matcher.group(3));
            return hours * 3600 + minutes * 60 + seconds;
        }
        return -1; // 未找到
    }

    /**
     * 将任意视频格式转换为MP4
     *
     * @param inputPath 输入视频文件路径
     * @return 转换后的MP4文件路径
     * @throws Exception 转换失败时抛出异常
     */
    public static String convertToMp4(String inputPath) throws Exception {
        return convertToMp4(inputPath, null);
    }

    /**
     * 将任意视频格式转换为MP4
     *
     * @param inputPath 输入视频文件路径
     * @param outputPath 输出MP4文件路径（可选）
     * @return 转换后的MP4文件路径
     * @throws Exception 转换失败时抛出异常
     */
    public static String convertToMp4(String inputPath, String outputPath) throws Exception {
        checkInitialized();
        return processVideoConversion(inputPath, outputPath);
    }

    /**
     * 将MultipartFile格式的视频转换为MP4
     *
     * @param file 上传的视频文件
     * @return 转换后的MP4文件路径
     * @throws Exception 转换失败时抛出异常
     */
    public static String convertToMp4(MultipartFile file) throws Exception {
        checkInitialized();
        Path tempInputFile = null;
        try {
            // 创建临时输入文件
            tempInputFile = saveMultipartFileToTemp(file);

            // 创建临时输出文件
            Path tempOutputFile = Files.createTempFile("converted_", ".mp4");
            String outputPath = tempOutputFile.toString();

            // 执行转换
            processVideoConversion(tempInputFile.toString(), outputPath);

            return outputPath;
        } finally {
            // 清理临时输入文件
            if (tempInputFile != null) {
                Files.deleteIfExists(tempInputFile);
            }
        }
    }

    /**
     * 核心视频转换处理逻辑
     */
    private static String processVideoConversion(String inputPath, String outputPath) throws Exception {
        if (outputPath == null || outputPath.isEmpty()) {
            // 自动生成输出路径（输入路径同名，后缀改为.mp4）
            Path inputFilePath = Paths.get(inputPath);
            String filename = inputFilePath.getFileName().toString();
            if (filename.contains(".")) {
                filename = filename.substring(0, filename.lastIndexOf('.'));
            }
            Path outputFilePath = inputFilePath.resolveSibling(filename + ".mp4");
            outputPath = outputFilePath.toString();
        }

        // 检查输出文件父目录是否存在
        Path outputPathObj = Paths.get(outputPath);
        Path outputDir = outputPathObj.getParent();
        if (outputDir != null && !Files.exists(outputDir)) {
            Files.createDirectories(outputDir);
        }

        // 构建转换命令
        String[] command = {
                FFMPEG_PATH,
                "-i", inputPath,
                "-c:v", "libx264",      // 使用H.264编码
                "-preset", "medium",    // 平衡速度和质量
                "-crf", "23",           // 质量控制（0-51，23是平衡值）
                "-c:a", "aac",          // 音频编码为AAC
                "-b:a", "128k",         // 音频比特率
                "-y",                   // 覆盖输出文件（不需要确认）
                outputPath
        };

        // 执行转换命令
        if (!executeFFmpegCommand(command, FFMPEG_TIMEOUT * 10, TimeUnit.SECONDS)) {
            throw new IOException("视频转换失败，请检查输入文件格式和FFmpeg日志");
        }

        // 检查输出文件是否存在
        if (!Files.exists(Paths.get(outputPath))) {
            throw new IOException("输出文件创建失败: " + outputPath);
        }

        return outputPath;
    }

    /**
     * 执行通用FFmpeg命令（重构版）
     *
     * @param command FFmpeg命令数组
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return 命令执行是否成功
     * @throws Exception
     */
    private static boolean executeFFmpegCommand(String[] command, long timeout, TimeUnit unit)
            throws IOException, InterruptedException {

        Process process = null;
        try {
            // 打印命令用于调试
            System.out.println("执行FFmpeg命令: " + String.join(" ", command));

            ProcessBuilder builder = new ProcessBuilder(command)
                    .redirectErrorStream(true);  // 合并标准输出和错误流

            process = builder.start();

            // 处理输出流（防止阻塞）
            Process finalProcess = process;
            Thread outputThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(finalProcess.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        // 可以添加处理转换进度的逻辑
                        System.out.println("[FFmpeg] " + line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            outputThread.start();

            // 等待进程完成
            boolean completed = process.waitFor(timeout, unit);
            if (!completed) {
                throw new IOException("FFmpeg执行超时 (" + timeout + " " + unit + ")");
            }

            // 等待输出线程结束
            outputThread.join(5000);

            // 检查退出码
            int exitCode = process.exitValue();
            if (exitCode != 0) {
                System.err.println("FFmpeg命令执行失败，退出码: " + exitCode);
                return false;
            }

            return true;
        } finally {
            // 确保进程终止
            if (process != null && process.isAlive()) {
                process.destroyForcibly();
            }
        }
    }


}